package cn.fang2chen.java.distributed.lock.framework.impl;

import cn.fang2chen.java.distributed.lock.framework.*;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;

/**
 * 分布式可重入锁
 * 进程获取到了远程锁，后面让当前的
 *
 * 远程锁为互斥锁，
 *  需要在远程锁加锁前判断当前锁是否是自己持有，
 *  在释放锁的时候需要判断本地锁是否已经都被解开,
 * 本地锁为可重入锁
 *
 * @author dcmini chen
 * @since 1.0.0
 */
public class DistributedReentrantLock implements DistributedLock, DistLockCopyAble {

    private final RemoteLockParam remoteLockParam;
    private final RemoteLockSynchronizer remoteLockSynchronizer;

    private final Sync sync;

    public DistributedReentrantLock(RemoteLockParam remoteLockParam, final RemoteLockSynchronizer remoteLockSynchronizer) {
        this.remoteLockParam = remoteLockParam;
        this.remoteLockSynchronizer = remoteLockSynchronizer;
        sync = new Sync(remoteLockSynchronizer);
    }

    public void lock() {
        sync.acquireDistributed(1, remoteLockParam);
    }

    public boolean tryLock() {
        return sync.tryAcquireDistributed(1, remoteLockParam);
    }

    public void unlock() {
        sync.releaseDistributed(1, remoteLockParam);
    }

    public Condition newCondition() {
        throw new UnsupportedOperationException();
    }

    public boolean isLocked() {
        return sync.isLocked(remoteLockParam);
    }

    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptiblyDistributed(1, remoteLockParam);
    }

    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanosDistributed(1, unit.toNanos(timeout), remoteLockParam);
    }

    @Override
    public long remainingTime() {
        return sync.remainingTime(remoteLockParam);
    }

    @Override
    public DistributedLock copy(RemoteLockParam remoteLockParam) {
        return new DistributedReentrantLock(remoteLockParam, remoteLockSynchronizer);
    }


    private static class Sync extends AbstractDistributedQueuedSynchronizer{

        private final RemoteLockSynchronizer remoteLockSynchronizer;

        public Sync(final RemoteLockSynchronizer remoteLockSynchronizer) {
            this.remoteLockSynchronizer = remoteLockSynchronizer;
        }

        @Override
        protected RemoteLockSynchronizer remoteLockSynchronizer() {
            return this.remoteLockSynchronizer;
        }

        @Override
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

        @Override
        protected void remoteLockAcquire(RemoteLockParam remoteLockParam) {
            if (!remoteLockSynchronizer().isOwner(remoteLockParam)) {
                super.remoteLockAcquire(remoteLockParam);
            }
        }

        @Override
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                        compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

        @Override
        protected boolean remoteLockRelease(RemoteLockParam remoteLockParam) {
            if (getState() == 0) {
                return super.remoteLockRelease(remoteLockParam);
            } else {
                return true;
            }
        }
    }

}
